package com.syf.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.enums.ApiErrorCode;
import com.baomidou.mybatisplus.extension.exceptions.ApiException;
import com.syf.domain.SysMenu;
import com.syf.domain.store.BsCompanyApply;
import com.syf.feign.JwtToken;
import com.syf.feign.OAuth2FeignClient;
import com.syf.model.LoginResult;
import com.syf.service.BsCompanyApplyService;
import com.syf.service.SysLoginService;
import com.syf.service.SysMenuService;
import com.syf.util.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Service;
import org.springframework.web.HttpRequestMethodNotSupportedException;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 *
 * @author 苏耀奋
 * @create 2021-07-26 13:19
 */
@Service
@Slf4j  //lombok.extern.slf4j
public class SysLoginServiceImpl implements SysLoginService {

    @Autowired
    private OAuth2FeignClient oAuth2FeignClient;  //使用feign远程调用  authorization-server服务 获取token

    @Autowired
    private BsCompanyApplyService bsCompanyApplyService;

    @Autowired
    private SysMenuService sysMenuService ;

    @Value("${basic.token:Basic Y29pbi1hcGk6Y29pbi1zZWNyZXQ=}")   //相当于token基础验证 加密出现的值。在Headers中可查看
    private String basicToken ;


    @Autowired
    private StringRedisTemplate redisTemplate ;
    /**
     * 登录的实现:
     *      根据用户名-password等 去授权服务器 验证 获取token
     *      利用token中存储的user_name用户id 查询该用户菜单
     *      从token中取出该用户所具有的权限
     *      将token缓存在redis中并设置过期时间
     *      封装需要的返回值数据(token、菜单、权限)，返回 封装对象
     *
     * @param username 用户名
     * @param password 用户的密码
     * @return 登录的结果
     */
    @Override
    public LoginResult login(String username, String password)  {
        log.info("用户{}开始登录,密码是: {}",username,password);  //变量username会插到{}位置
        System.out.println("前端传递的密码： "+password);  //前端传递的密码： 123456【并没有在前端经过MD5进行加密】

        // 1 获取token 需要远程调用authorization-server 该服务
        ResponseEntity<JwtToken> tokenResponseEntity = oAuth2FeignClient.getToken("password", username, password, "admin_type", basicToken);
        if(tokenResponseEntity.getStatusCode()!= HttpStatus.OK){ //token状态码
            throw new ApiException(ApiErrorCode.FAILED) ;
        }
        JwtToken jwtToken = tokenResponseEntity.getBody();  //jwtToken对象
        log.info("远程调用授权服务器成功,获取的token为{}", JSON.toJSONString(jwtToken,true));
        String token = jwtToken.getAccessToken() ; //获取真正的access_token值

        // 2 查询我们的菜单数据
         /*真正的access_token值 转化后【token中已经有我们的user_name用户ID和权限数据了】
            {
              "exp": 1627871022,
              "user_name": "1014077772628398082", 用户ID
              "authorities": [   权限数据，无需查询
                "ROLE_USER",
                "Toliyn_123"...
              ],
              "jti": "afc19056-ab96-414c-ab85-e1942724dfdb",
              "client_id": "coin-api",
              "scope": [
                "all"
              ]
            }
         */
        Jwt jwt = JwtHelper.decode(token); //解析access_token值【字符串token值】
        String jwtJsonStr = jwt.getClaims(); //获取JWT Json值(如下)
        JSONObject jwtJson = JSON.parseObject(jwtJsonStr); //JWT对象
        Long userId = Long.valueOf(jwtJson.getString("user_name")) ;
        List<SysMenu> menus = sysMenuService.getMenusByUserId(userId); //根据用户ID查询菜单数据(连表)

        // 3 权限数据怎么查询 -- 不需要查询的，因为我们的jwt 里面已经包含了
        JSONArray authoritiesJsonArray = jwtJson.getJSONArray("authorities"); //Json权限 数组
        List<SimpleGrantedAuthority> authorities = authoritiesJsonArray.stream() // 组装我们的权限数据,生成Stream数据源
                .map(authorityJson->new SimpleGrantedAuthority(authorityJson.toString())) //流Stream 转换成 map数据结构,再将map容器中的数据 转化成 SimpleGrantedAuthority【authorityJson只是一个临时表示map容器数据的变量，不固定】
                .collect(Collectors.toList()); //最后收集到Collectors->List容器中

        // [1]将该token 存储在redis 里面，配置我们的网关做jwt验证的操作【以token作为key,value=""】
        redisTemplate.opsForValue().set(token,"", jwtToken.getExpiresIn() , TimeUnit.SECONDS); //key:token变量,value="",过期时间,时间单位:秒

        //查询软件厂商ID
        BsCompanyApply bsCompanyApply = bsCompanyApplyService.selByCode(username);
        Integer companyId = 2;
        if(bsCompanyApply !=null){
            companyId = bsCompanyApply.getId();
        }

        // [2]我们返回给前端的Token 数据，少一个bearer：
        return new LoginResult(jwtToken.getTokenType()+" "+token, menus, authorities,companyId);
    }
}
